Participation Distribution in Committee Selection¶
Executive Summary¶
In the following computer experiments, we aim to understand the distribution of selections in a committee when varying sizes of the participant pool of SPOs and the committee. We show that the "pigeonhole principle" helps us interpret the results and understand the finite distribution of the committee seats assigned to participants as a function of stake, group, and committee sizes.
The experiment is designed to:
- Sample without replacement a group of participants from the population and
- Calculate the stake weight for each participant, which is the stake normalized over the group to sum to 1.
- Assign a committee of the fixed group size based on the stake weight of each using random selection with replacement.
- Analyze the relationship and distribution of committee selection with group size.
We conducted the experiments with varying sizes (100, 200, ..., 500) of groups and committees. The results are visualized through plots of committee assignments where we vary the group size to see how the committee selection and seat count changes.
The results show that some group members with smaller stake weights may not (ever?) get selected for committee seats. With repeated trials where a new committee is selected, called an epoch, and assuming nonzero stake weight, there is nonzero probability of selecting any participant in the long run. However, in the short term, there is a significant chance that some participants will not ever get selected, almost surely. This is a natural outcome of the selection process with a discrete and finite number of seats. This is a manifestation of this committee selection process as it currently stands.
# %%
# Load the required libraries
from participation_lib import (
np,
pd,
plt,
sns,
load_data,
get_stake_distribution,
assign_commitee_plus,
simulate,
std_error,
plot_group_to_committee_index,
plot_selection_count_vs_stake,
plot_committee_selection_counts,
plot_committee_selection_seat_cutoff,
)
# %%
# Load the Data: The population of registered SPOs
population = load_data("../data/pooltool-cleaned.csv")
print(population.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3056 entries, 0 to 3055 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 3056 non-null object 1 stake 3056 non-null int64 2 stake_percent 3056 non-null float64 dtypes: float64(1), int64(1), object(1) memory usage: 71.8+ KB None
# %%
population.describe()
| stake | stake_percent | |
|---|---|---|
| count | 3.056000e+03 | 3056.000000 |
| mean | 7.305314e+06 | 0.032723 |
| std | 1.648449e+07 | 0.073839 |
| min | 0.000000e+00 | 0.000000 |
| 25% | 5.265000e+02 | 0.000002 |
| 50% | 5.692500e+04 | 0.000255 |
| 75% | 3.282500e+06 | 0.014703 |
| max | 1.054300e+08 | 0.472250 |
# %%
# Let's now sample a group of participants from the population
# and calculate the stake weight for each participant.
group_size = 100
group_stakes = get_stake_distribution(
population,
group_size=group_size,
num_iter=1000,
plot_it=True,
)
print(group_stakes)
stake stake_weight 0 7.283987e+07 9.217518e-02 1 6.846427e+07 8.663808e-02 2 6.461900e+07 8.177208e-02 3 5.964222e+07 7.547422e-02 4 5.455957e+07 6.904238e-02 .. ... ... 95 1.986700e+01 2.514069e-08 96 1.168200e+01 1.478298e-08 97 6.484000e+00 8.205175e-09 98 3.224000e+00 4.079809e-09 99 1.600000e+00 2.024719e-09 [100 rows x 2 columns]
# %%
print(group_stakes.describe())
stake stake_weight count 1.000000e+02 1.000000e+02 mean 7.902330e+06 1.000000e-02 std 1.665953e+07 2.108180e-02 min 1.600000e+00 2.024719e-09 25% 1.837206e+03 2.324892e-06 50% 1.513475e+05 1.915227e-04 75% 4.831149e+06 6.113575e-03 max 7.283987e+07 9.217518e-02
# %%
# Let's now assign a committee of the fixed group_size
# based on the stake weight of each
results = assign_commitee_plus(
group_stakes,
committee_size=group_size,
num_iter=1000,
)
# %%
# Let's now create a plots of committee assignments where we vary
# the group size over {100, 200, 300, 400, 500} and see how the
# committee selection and seat count changes.
# Initialize Parameters:
# comm_sizes = [100] # vary over committee size, k
# group_sizes = [100] # vary over group size, n
comm_sizes = [100, 200, 300, 400, 500] # vary over committee size, k
group_sizes = [100, 200, 300, 400, 500] # vary over group size, n
num_iter = 1 # Number of iterations for Monte Carlo simulation
# Note that the number of iterations here can be interpreted as the number
# of selection rounds for the committee, which we call an epoch.
# If we have a new epoch per day, then 1000 iterations is about 3 years.
# %%
# Call the function
sim_results_df = simulate(
population,
comm_sizes,
group_sizes,
num_iter,
plot_it=True,
)
Committee Size = 100 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 200 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 300 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 400 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 500 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
# %%
# committee_seats_df = committee_seats_df.swaplevel(axis=1).sort_index(axis=1)
# %%
# Extract the data for plotting
col_index = sim_results_df.columns
commitee_sizes = [
int(col.split("=")[1].strip()) for col in col_index.get_level_values(0).unique()
]
group_sizes = [
int(col.split("=")[1].strip()) for col in col_index.get_level_values(1).unique()
]
# %%
# Plot the percentage of group participants excluded from a committee
# of a given size vs. different group sizes
fig, ax = plt.subplots(figsize=(12, 8))
sns.set(style="whitegrid")
for committee_size in commitee_sizes:
committee_label = f"Committee Size = {committee_size}"
committee_voters = sim_results_df.loc["Distinct Voters", committee_label]
mean_values = committee_voters.loc["mean"]
std_dev_values = committee_voters.loc["sd"]
# Calculate the percentage of participants not selected for committee seats
not_selected_percentages = (1.0 - mean_values / group_sizes) * 100
not_selected_percentages.name = "Excluded (%)"
# Create a DataFrame for easier plotting with seaborn
plot_data = pd.DataFrame(
{
"Group Size": group_sizes,
"Percentage Excluded": not_selected_percentages,
"Std Dev": std_dev_values,
}
)
# Plot the main line without error bars
sns.lineplot(
x="Group Size",
y="Percentage Excluded",
data=plot_data,
marker="o",
label=committee_label,
ax=ax,
)
ax.set_ylabel("Percentage Excluded")
ax.set_xlabel("Group Size")
ax.legend(title="Committee Size")
plt.title("Percentage of Group Participants Not Selected for Committee Seats")
plt.grid(True)
plt.show()
# %%
sim_results_df.loc["Distinct Voters", :]
| Committee Size | Committee Size = 100 | Committee Size = 200 | ... | Committee Size = 400 | Committee Size = 500 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Group Size | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | ... | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 |
| mean | 25.0 | 37.0 | 50.0 | 52.0 | 58.0 | 31.0 | 51.0 | 71.0 | 81.0 | 91.0 | ... | 35.0 | 66.0 | 85.0 | 95.0 | 103.0 | 39.0 | 68.0 | 76.0 | 121.0 | 123.0 |
| sd | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 rows × 25 columns
# %%
# Plot the committee selection counts distribution
fig = plt.figure(figsize=(12, 8))
plot_data = sim_results_df.loc["Committee Seats"].loc["mean"]
for c, g in plot_data.index:
y = plot_data.loc[(c, g)]
x = y.index
n_c = int(c.split("=")[1].strip())
n_g = int(g.split("=")[1].strip())
colors = sns.color_palette("tab20", len(plot_data.index))
color_idx = list(plot_data.index).index((c, g))
plt.bar(x, y, alpha=0.7, color=colors[color_idx], label=f"{n_c}, {n_g}")
plt.xlabel("Participant Index")
plt.ylabel("Committee Seat Count (average)")
plt.title("Committee Seat Count for Participants")
plt.legend(title="Committee Size, Group Size")
plt.xlim(0, 200)
plt.show()
# %%
# Distinct Voters
committee_voters = sim_results_df.loc["Distinct Voters"]
# Create a DataFrame row from the computed percentages
mean_values = committee_voters.loc["mean"]
std_dev_values = committee_voters.loc["sd"]
# Calculate the percentage of participants not selected for committee seats
print("Percentage of Group Participants Not Selected for Committee Seats:")
committee_participation = pd.concat([mean_values, std_dev_values], axis=1)
# committee_participation.columns = ["Mean", "Std Dev"]
print(committee_participation)
Percentage of Group Participants Not Selected for Committee Seats:
mean sd
Committee Size Group Size
Committee Size = 100 Group Size = 100 25.0 0.0
Group Size = 200 37.0 0.0
Group Size = 300 50.0 0.0
Group Size = 400 52.0 0.0
Group Size = 500 58.0 0.0
Committee Size = 200 Group Size = 100 31.0 0.0
Group Size = 200 51.0 0.0
Group Size = 300 71.0 0.0
Group Size = 400 81.0 0.0
Group Size = 500 91.0 0.0
Committee Size = 300 Group Size = 100 40.0 0.0
Group Size = 200 45.0 0.0
Group Size = 300 76.0 0.0
Group Size = 400 91.0 0.0
Group Size = 500 109.0 0.0
Committee Size = 400 Group Size = 100 35.0 0.0
Group Size = 200 66.0 0.0
Group Size = 300 85.0 0.0
Group Size = 400 95.0 0.0
Group Size = 500 103.0 0.0
Committee Size = 500 Group Size = 100 39.0 0.0
Group Size = 200 68.0 0.0
Group Size = 300 76.0 0.0
Group Size = 400 121.0 0.0
Group Size = 500 123.0 0.0
# %%
# Prepare the DataFrame for concatenation with the other simulation results
committee_participation = committee_participation.T
committee_participation.index = pd.MultiIndex.from_tuples(
[("Committee Participation %", "mean"), ("Committee Participation %", "sd")]
)
# Concatenate this new row to the simulation results DataFrame
sim_results_df = pd.concat([sim_results_df, committee_participation], axis=0)
sim_results_df
| Committee Size | Committee Size = 100 | Committee Size = 200 | ... | Committee Size = 400 | Committee Size = 500 | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Group Size | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | ... | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | |
| Distinct Voters | mean | 25.0 | 37.0 | 50.0 | 52.0 | 58.0 | 31.0 | 51.0 | 71.0 | 81.0 | 91.0 | ... | 35.0 | 66.0 | 85.0 | 95.0 | 103.0 | 39.0 | 68.0 | 76.0 | 121.0 | 123.0 |
| sd | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
| Committee Seats | mean | 0 7.0 1 6.0 2 15.0 3 5.0 4 ... | 0 5.0 1 3.0 2 5.0 3 5.0 4 ... | 0 4.0 1 1.0 2 3.0 3 3.0 4 ... | 0 5.0 1 2.0 2 3.0 3 3.0 4 ... | 0 7.0 1 0.0 2 1.0 3 2.0 4 ... | 0 19.0 1 18.0 2 16.0 3 21.0 4 ... | 0 3.0 1 10.0 2 10.0 3 9.... | 0 5.0 1 8.0 2 7.0 3 13.... | 0 5.0 1 4.0 2 4.0 3 4.0 4 ... | 0 4.0 1 3.0 2 6.0 3 3.0 4 ... | ... | 0 50.0 1 46.0 2 42.0 3 37.0 4 ... | 0 12.0 1 20.0 2 17.0 3 21.... | 0 16.0 1 8.0 2 11.0 3 9.... | 0 8.0 1 12.0 2 13.0 3 13.... | 0 4.0 1 10.0 2 14.0 3 7.... | 0 41.0 1 41.0 2 43.0 3 40.0 4 ... | 0 29.0 1 29.0 2 20.0 3 17.... | 0 22.0 1 21.0 2 13.0 3 14.... | 0 11.0 1 12.0 2 7.0 3 7.... | 0 13.0 1 8.0 2 9.0 3 8.... |
| Committee Participation % | mean | 25.0 | 37.0 | 50.0 | 52.0 | 58.0 | 31.0 | 51.0 | 71.0 | 81.0 | 91.0 | ... | 35.0 | 66.0 | 85.0 | 95.0 | 103.0 | 39.0 | 68.0 | 76.0 | 121.0 | 123.0 |
| sd | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
5 rows × 25 columns
# %%
# Let's now create a plots of committee assignments where we vary
# the group size over {100, 200, 300, 400, 500} and see how the
# committee selection and seat count changes.
# Initialize Parameters:
# comm_sizes = [100] # vary over committee size, k
# group_sizes = [100] # vary over group size, n
comm_sizes = [100, 200, 300, 400, 500] # vary over committee size, k
group_sizes = [100, 200, 300, 400, 500] # vary over group size, n
num_iter = 30 # Number of iterations for Monte Carlo simulation
# Note that the number of iterations here can be interpreted as the number
# of selection rounds for the committee, which we call an epoch.
# If we have a new epoch per day, then 1000 iterations is about 3 years.
# %%
# Call the function
sim_results_df = simulate(
population,
comm_sizes,
group_sizes,
num_iter,
plot_it=True,
)
Committee Size = 100 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 200 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 300 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 400 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
Committee Size = 500 Group Size = 100
Group Size = 200
Group Size = 300
Group Size = 400
Group Size = 500
# %%
# committee_seats_df = committee_seats_df.swaplevel(axis=1).sort_index(axis=1)
# %%
# Extract the data for plotting
col_index = sim_results_df.columns
commitee_sizes = [
int(col.split("=")[1].strip()) for col in col_index.get_level_values(0).unique()
]
group_sizes = [
int(col.split("=")[1].strip()) for col in col_index.get_level_values(1).unique()
]
# %%
# Plot the percentage of group participants excluded from a committee
# of a given size vs. different group sizes
fig, ax = plt.subplots(figsize=(12, 8))
sns.set(style="whitegrid")
for committee_size in commitee_sizes:
committee_label = f"Committee Size = {committee_size}"
committee_voters = sim_results_df.loc["Distinct Voters", committee_label]
mean_values = committee_voters.loc["mean"]
std_dev_values = committee_voters.loc["sd"]
# Calculate the percentage of participants not selected for committee seats
not_selected_percentages = (1.0 - mean_values / group_sizes) * 100
not_selected_percentages.name = "Excluded (%)"
# Create a DataFrame for easier plotting with seaborn
plot_data = pd.DataFrame(
{
"Group Size": group_sizes,
"Percentage Excluded": not_selected_percentages,
"Std Dev": std_dev_values,
}
)
# Plot the main line without error bars
sns.lineplot(
x="Group Size",
y="Percentage Excluded",
data=plot_data,
marker="o",
label=committee_label,
ax=ax,
)
ax.set_ylabel("Percentage Excluded")
ax.set_xlabel("Group Size")
ax.legend(title="Committee Size")
plt.title("Percentage of Group Participants Not Selected for Committee Seats")
plt.grid(True)
plt.show()
# %%
sim_results_df.loc["Distinct Voters", :]
| Committee Size | Committee Size = 100 | Committee Size = 200 | ... | Committee Size = 400 | Committee Size = 500 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Group Size | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | ... | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 |
| mean | 25.466667 | 40.6 | 50.5 | 57.2 | 63.733333 | 31.366667 | 50.9 | 66.966667 | 79.133333 | 91.133333 | ... | 37.2 | 61.566667 | 81.166667 | 99.766667 | 118.466667 | 37.8 | 62.766667 | 86.466667 | 108.366667 | 125.966667 |
| sd | 2.692376 | 2.332381 | 2.459675 | 3.673327 | 3.604935 | 2.316367 | 2.785079 | 3.25047 | 4.514667 | 4.709801 | ... | 1.83303 | 3.58407 | 3.387067 | 4.038839 | 4.68852 | 2.181742 | 3.018646 | 2.629744 | 4.370228 | 5.552677 |
2 rows × 25 columns
# %%
# Plot the committee selection counts distribution
fig = plt.figure(figsize=(12, 8))
plot_data = sim_results_df.loc["Committee Seats"].loc["mean"]
for c, g in plot_data.index:
y = plot_data.loc[(c, g)]
x = y.index
n_c = int(c.split("=")[1].strip())
n_g = int(g.split("=")[1].strip())
colors = sns.color_palette("tab20", len(plot_data.index))
color_idx = list(plot_data.index).index((c, g))
plt.bar(x, y, alpha=0.7, color=colors[color_idx], label=f"{n_c}, {n_g}")
plt.xlabel("Participant Index")
plt.ylabel("Committee Seat Count (average)")
plt.title("Committee Seat Count for Participants")
plt.legend(title="Committee Size, Group Size")
plt.xlim(0, 200)
plt.show()
# %%
# Distinct Voters
committee_voters = sim_results_df.loc["Distinct Voters"]
# Create a DataFrame row from the computed percentages
mean_values = committee_voters.loc["mean"]
std_dev_values = committee_voters.loc["sd"]
# Calculate the percentage of participants not selected for committee seats
print("Percentage of Group Participants Not Selected for Committee Seats:")
committee_participation = pd.concat([mean_values, std_dev_values], axis=1)
# committee_participation.columns = ["Mean", "Std Dev"]
print(committee_participation)
Percentage of Group Participants Not Selected for Committee Seats:
mean sd
Committee Size Group Size
Committee Size = 100 Group Size = 100 25.466667 2.692376
Group Size = 200 40.6 2.332381
Group Size = 300 50.5 2.459675
Group Size = 400 57.2 3.673327
Group Size = 500 63.733333 3.604935
Committee Size = 200 Group Size = 100 31.366667 2.316367
Group Size = 200 50.9 2.785079
Group Size = 300 66.966667 3.25047
Group Size = 400 79.133333 4.514667
Group Size = 500 91.133333 4.709801
Committee Size = 300 Group Size = 100 33.2 2.181742
Group Size = 200 57.033333 3.25047
Group Size = 300 75.5 3.442383
Group Size = 400 90.666667 3.486482
Group Size = 500 108.1 5.127377
Committee Size = 400 Group Size = 100 37.2 1.83303
Group Size = 200 61.566667 3.58407
Group Size = 300 81.166667 3.387067
Group Size = 400 99.766667 4.038839
Group Size = 500 118.466667 4.68852
Committee Size = 500 Group Size = 100 37.8 2.181742
Group Size = 200 62.766667 3.018646
Group Size = 300 86.466667 2.629744
Group Size = 400 108.366667 4.370228
Group Size = 500 125.966667 5.552677
# %%
# Prepare the DataFrame for concatenation with the other simulation results
committee_participation = committee_participation.T
committee_participation.index = pd.MultiIndex.from_tuples(
[("Committee Participation %", "mean"), ("Committee Participation %", "sd")]
)
# Concatenate this new row to the simulation results DataFrame
sim_results_df = pd.concat([sim_results_df, committee_participation], axis=0)
sim_results_df
| Committee Size | Committee Size = 100 | Committee Size = 200 | ... | Committee Size = 400 | Committee Size = 500 | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Group Size | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | ... | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | Group Size = 100 | Group Size = 200 | Group Size = 300 | Group Size = 400 | Group Size = 500 | |
| Distinct Voters | mean | 25.466667 | 40.6 | 50.5 | 57.2 | 63.733333 | 31.366667 | 50.9 | 66.966667 | 79.133333 | 91.133333 | ... | 37.2 | 61.566667 | 81.166667 | 99.766667 | 118.466667 | 37.8 | 62.766667 | 86.466667 | 108.366667 | 125.966667 |
| sd | 2.692376 | 2.332381 | 2.459675 | 3.673327 | 3.604935 | 2.316367 | 2.785079 | 3.25047 | 4.514667 | 4.709801 | ... | 1.83303 | 3.58407 | 3.387067 | 4.038839 | 4.68852 | 2.181742 | 3.018646 | 2.629744 | 4.370228 | 5.552677 | |
| Committee Seats | mean | 0 8.600000 1 9.900000 2 8.900000 3... | 0 4.666667 1 4.266667 2 4.33333... | 0 3.000000 1 2.800000 2 2.96666... | 0 2.866667 1 2.333333 2 1.90000... | 0 2.166667 1 1.700000 2 2.10000... | 0 16.433333 1 15.400000 2 15.86666... | 0 9.233333 1 9.266667 2 8.30000... | 0 6.433333 1 6.466667 2 7.03333... | 0 4.933333 1 4.033333 2 4.23333... | 0 4.766667 1 3.966667 2 3.86666... | ... | 0 40.033333 1 37.800000 2 33.30000... | 0 19.433333 1 17.933333 2 19.00... | 0 14.200000 1 11.833333 2 13.03... | 0 11.300000 1 8.266667 2 9.26... | 0 7.833333 1 7.966667 2 7.23333... | 0 47.366667 1 43.133333 2 41.26666... | 0 24.500000 1 23.533333 2 21.13... | 0 17.533333 1 15.033333 2 16.13... | 0 12.066667 1 11.800000 2 11.53... | 0 9.800000 1 10.366667 2 8.80... |
| Committee Participation % | mean | 25.466667 | 40.6 | 50.5 | 57.2 | 63.733333 | 31.366667 | 50.9 | 66.966667 | 79.133333 | 91.133333 | ... | 37.2 | 61.566667 | 81.166667 | 99.766667 | 118.466667 | 37.8 | 62.766667 | 86.466667 | 108.366667 | 125.966667 |
| sd | 2.692376 | 2.332381 | 2.459675 | 3.673327 | 3.604935 | 2.316367 | 2.785079 | 3.25047 | 4.514667 | 4.709801 | ... | 1.83303 | 3.58407 | 3.387067 | 4.038839 | 4.68852 | 2.181742 | 3.018646 | 2.629744 | 4.370228 | 5.552677 | |
5 rows × 25 columns
# %%
# Save the results to an Excel file
output_file = "../data/participation_run_results.xlsx"
sim_results_df.to_excel(output_file)
print(f"Results saved to {output_file}")
Results saved to ../data/participation_run_results.xlsx